home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / PacMan / Monster.m < prev    next >
Encoding:
Text File  |  1992-07-24  |  9.1 KB  |  337 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import <libc.h>
  5. #import "Monster.h"
  6. #import "GameBrain.h"
  7. #import "Player.h"
  8. #import "Maze.h"
  9. #import <appkit/NXImage.h>
  10. #import <appkit/Application.h>
  11. #import <appkit/graphics.h>
  12.  
  13. // Get defs of static data arrays which control movement choices
  14. #import "MonsterMovement.h"
  15.  
  16.  
  17. @implementation Monster
  18.  
  19. - init
  20. {
  21.     return [self initGhost:0 player:nil maze:nil at:0 :0];
  22. }
  23.  
  24. - initGhost:(int)num player:(id)pac maze:(id)world at:(int)sx :(int)sy
  25. {    // initialize all instance vars
  26.     [super init];
  27.     ghosts = [NXImage findImageNamed:"Ghosts.tiff"];
  28.     lastx = - 4 * GHOST_SIZE;
  29.     lasty = - 4 * GHOST_SIZE;
  30.     player = pac;
  31.     maze = world;
  32.     eatable = NO;
  33.     state = HOVER;
  34.     myX = sx * GHOST_SIZE; myY = sy * GHOST_SIZE;
  35.     myColor = num;
  36.     px = 2; py = 0;
  37.     loops = 0;
  38.     fix = NO;
  39.     
  40.     return self;
  41. }
  42.  
  43. - (BOOL)canBeEaten        // returns "eatable".
  44. { return eatable; }
  45.  
  46. - move:sender        // move one frame
  47. {
  48.     // use current state to decide which type of movement to do
  49.     switch (state) {
  50.         case HOVER : {
  51.             [self hover];
  52.             break;
  53.         }
  54.         case CHASE : {
  55.             [self chase];
  56.             break;
  57.         }
  58.         case RUN   : {
  59.             [self runAway];
  60.             break;
  61.         }
  62.         case HOME  : {
  63.             [self goHome];
  64.             break;
  65.         }
  66.         default    : {    // no movement, since illegal state
  67.             // (we should _never_ get here!)
  68.             px = 0; py = 0;
  69.             break;
  70.         }
  71.     }
  72.     return self;
  73. }
  74.  
  75. - hover                // hover moves solid ghost when in ghost chamber
  76. {
  77.     int door_x, door_y;
  78.     int k;
  79.     
  80.     [maze doorPosition:&door_x :&door_y];
  81.  
  82.     if (myX == door_x) {
  83.         if (myY >= (door_y + GHOST_SIZE)) {
  84. // The ghost is now completely outside the box; we will
  85. // change its state so that it follows the player around
  86. // (or runs away if power dot was eaten)
  87.             if (eatable) {
  88.                 state = RUN;
  89.                 [self runAway];
  90.             } else {
  91.                 state = CHASE;
  92.                 [self chase];
  93.             }
  94.             return self;
  95.         } else if (myY >= (door_y - 1 - GHOST_SIZE)) {
  96. // The ghost is directly underneath the door to the outside.
  97. // Send it out if more than 3 loops made, or 50% chance to leave if
  98. // at least one loop is complete.
  99.             if ((++loops) > 1) {
  100.                 if ((loops > 3) || ((random() & 0x0f) > 7) || eatable) {
  101.                     if (eatable) py = 1; // keep right speed
  102.                     else py = 2;
  103.                     px = 0;
  104.                     return self;
  105.     }    }    }    }
  106. // The rest of the method drives the ghost around the
  107. // box in a counterclockwise pattern.
  108.     k = 0;
  109.     if (px > 0) {
  110.         if ([maze monsterWall:(myX + 1 + GHOST_SIZE) :myY])
  111.             { k = 1; px = 0; py = 2; }
  112.     } else if (px < 0) {
  113.         if ([maze monsterWall:(myX - 2) :myY])
  114.             { k = 1; px = 0; py = -2; }
  115.     } else if (py > 0) {
  116.         if ([maze monsterWall:myX :(myY + 1 + GHOST_SIZE)])
  117.             { k = 1; px = -2; py = 0; }
  118.     } else if ([maze monsterWall:myX :(myY - 2)])
  119.             { k = 1; px = 2; py = 0; }
  120.     if (eatable && k) {
  121.         px /= 2;
  122.         py /= 2;
  123.     }
  124.     if ((abs(py) == 2) && (myY & 0x1)) fix = YES;    // keep synced
  125.     if ((abs(px) == 2) && (myX & 0x1)) fix = YES;    // keep synced
  126.     return self;
  127. }
  128.  
  129. - runAway            // ghost runs away at 1/2 speed after power pill
  130. {
  131.     int pac_x = [player xpos];
  132.     int pac_y = [player ypos];
  133.     register int tdir = 0x0f, sense;
  134.     int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
  135.     
  136.  
  137.     if (dx) tdir &= 0x03; // only allow l/r to sync us up
  138.     if (dy) tdir &= 0x0c; // only allow u/d to sync us up
  139. // first, find the directions in which this ghost can go
  140.     if ([maze playerWall:(myX+GHOST_SIZE) :myY]    || (px < 0)) tdir &= ~0x01;
  141.     if ([maze playerWall:(myX-1) :myY]             || (px > 0)) tdir &= ~0x02;
  142.     if ([maze playerWall:myX     :(myY-1)]         || (py > 0)) tdir &= ~0x04;
  143.     if ([maze playerWall:myX :(myY+GHOST_SIZE)]    || (py < 0)) tdir &= ~0x08;
  144.  
  145. // now choose the new direction for the ghost
  146.     if ((random() & 0x0f) > 4)
  147.         sense = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
  148.     else sense = random() & 0x07;
  149.     px = rxvec[tdir][sense];
  150.     py = ryvec[tdir][sense];
  151.     return self;
  152. }
  153.  
  154. - chase                // ghost chases player down
  155. {
  156.     int pac_x = [player xpos];
  157.     int pac_y = [player ypos];
  158.     register int tdir = 0x0f;
  159.     register int sense;
  160.     int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
  161.     
  162. // sync position to block boundaries
  163.     if (px < 0) {
  164.       if (dx > 1) return self;
  165.       if (dx == 1) { px = -1; return self; }
  166.     } else if (px > 0) {
  167.       if ((dx < GHOST_SIZE - 1) && dx) return self;
  168.       if (dx == GHOST_SIZE - 1) { px = 1; return self; }
  169.     } else if (py < 0) {
  170.       if (dy > 1) return self;
  171.       if (dy == 1) { py = -1; return self; }
  172.     } else if (py > 0) {
  173.       if ((dy < GHOST_SIZE - 1) && dy) return self;
  174.       if (dy == GHOST_SIZE - 1) { py = 1; return self; }
  175.     }
  176.  
  177. // first, find the directions in which this ghost can go
  178.     if ([maze playerWall:(myX+2+GHOST_SIZE) :myY]    || (px < 0)) tdir &= ~0x01;
  179.     if ([maze playerWall:(myX-2) :myY]                || (px > 0)) tdir &= ~0x02;
  180.     if ([maze playerWall:myX     :(myY-2)]            || (py > 0)) tdir &= ~0x04;
  181.     if ([maze playerWall:myX :(myY+2+GHOST_SIZE)]    || (py < 0)) tdir &= ~0x08;
  182.  
  183. // now choose the new direction for the ghost
  184.     if ((random() & 0x0f) > 4) // 75% of time, it's algorithmic
  185.         sense = find[sgn(pac_y - myY) + 1][sgn(pac_x - myX) + 1];
  186.     else sense = random() & 0x07;
  187.     px = fxvec[tdir][sense];
  188.     py = fyvec[tdir][sense];
  189.     return self;
  190. }
  191.  
  192. - goHome            // used to make the eyes run to ghost chamber at 2x speed
  193. {
  194.     int door_x, door_y;
  195.     register int tdir = 0x0f, sense;
  196.     int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
  197.  
  198.     [maze doorPosition:&door_x :&door_y];
  199.     // there, we'll "catch" it and suck it into the box.
  200.     if (myX == door_x) { // using GHOST_SIZE * 2 moves it to bottom of box
  201.         if ((myY >= door_y - GHOST_SIZE * 2) && (myY <= door_y + GHOST_SIZE)) {
  202.             if ((myY >= door_y - GHOST_SIZE) && (myY <= door_y + GHOST_SIZE)) {
  203. // The ghost is right above the door to the ghost box.
  204. // We'll send it down into the box.  We're assuming
  205. // here that the ghost box is below the door.
  206. // If not, the results will be unpredictable.
  207.                 px = 0;
  208.                 if (dy && (dy < 4)) py = -dy; // sync to blocks
  209.                 else py = -4;
  210.             } else if (myY == (door_y - GHOST_SIZE * 2)) {
  211. // The ghost is all the way inside the box.  Here it'll
  212. // be "reborn" -- its state will be changed to that of a
  213. // solid ghost hovering inside the ghost box.
  214.                 state = HOVER;
  215.                 loops = 0;
  216.                 eatable = NO;
  217.                 px = 2;
  218.                 py = 0;
  219.             }
  220.             return self;
  221.     }    }
  222.  
  223.     door_y += GHOST_SIZE;    // seek the block above the door; when ghost gets
  224. // now, sync ghost to maze blocks
  225.     if (px < 0) {
  226.       if (dx > 3) return self;
  227.       if (dx) { px = -dx; return self; }
  228.     } else if (px > 0) {
  229.       if ((dx < GHOST_SIZE - 3) && dx) return self;
  230.       else if (dx) { px = GHOST_SIZE - dx; return self; }
  231.     } else if (py < 0) {
  232.       if (dy > 3) return self;
  233.       if (dy) { py = -dy; return self; }
  234.     } else if (py > 0) {
  235.       if ((dy < GHOST_SIZE - 3) && dy) return self;
  236.       else if (dy) { py = GHOST_SIZE - dy; return self; }
  237.     }
  238.  
  239.     if (dx) tdir &= 0x03;
  240.     if (dy) tdir &= 0x0c;
  241.     
  242. // first, find the directions in which this ghost can go
  243.     if ([maze monsterWall:(myX+4+GHOST_SIZE) :myY]    || (px < 0)) tdir &= ~0x01;
  244.     if ([maze monsterWall:(myX-4) :myY]                || (px > 0)) tdir &= ~0x02;
  245.     if ([maze monsterWall:myX     :(myY-4)]            || (py > 0)) tdir &= ~0x04;
  246.     if ([maze monsterWall:myX :(myY+4+GHOST_SIZE)]    || (py < 0)) tdir &= ~0x08;
  247.  
  248. // now choose the new direction for the ghost--not random!
  249.     sense = find[sgn(door_y - myY) + 1][sgn(door_x - myX) + 1];
  250.     px = pxvec[tdir][sense];
  251.     py = pyvec[tdir][sense];
  252.     return self;
  253. }
  254.  
  255. - powerDot:(BOOL)eat    // called when power dots are eaten or run out.
  256. {
  257.     if (state != HOME) eatable = eat;
  258.     if ((state == CHASE) && (eat == YES)) {
  259.         state = RUN;
  260.         px  = 0; py = 0;    // allows quick reverse of direction
  261.         [self runAway];
  262.     }
  263.     if ((state == RUN)   && (eat ==  NO)) {
  264.         state = CHASE;
  265.         if (abs(px) < 2) px *= 2; // ifs should actually be unnecessary...
  266.         if (abs(py) < 2) py *= 2;
  267.         [self chase];
  268.     }
  269.     if (state == HOVER) {    // speed up or slow down hovering ghost
  270.         // has to happen here, since hover leaves it alone until it hits
  271.         // an edge of the box...
  272.         if (eat) {
  273.             if (abs(px) == 2) px /=2;
  274.             if (abs(py) == 2) py /= 2;
  275.         } else {
  276.             if (abs(px) == 1) px *=2;
  277.             if (abs(py) == 1) py *= 2;
  278.     }    }
  279.     if (eat) {
  280.         powerState = DOT_EATEN;
  281.         powerTime = 0;
  282.         [[NXApp delegate] resetGhostScore];
  283.     } else  powerState = DOT_NONE;
  284.     return self;
  285. }
  286.  
  287. - (int)munch            // called when pac intersects ghost
  288. {
  289.     if (state == HOME) return HARMLESS;
  290.     if (!eatable) return NO;
  291.     px  = 0; py  = 0;    // allows reverse of direction
  292.     state = HOME;
  293.     [self goHome];
  294.     eatable = NO;
  295.     return YES;
  296. }
  297.  
  298. - moveOneFrame
  299. {
  300.     [super moveOneFrame];        // move the ghost
  301.     if (fix) {
  302.         myY -= sgn(py);    // fix for when hovering -- to re-sync
  303.         myX -= sgn(px);
  304.         fix = 0;
  305.     }
  306.     return self;
  307. }
  308.  
  309. - renderAt:(int)posx :(int)posy move:(BOOL)moveOk    // draw ghost
  310. {
  311.     int col = myColor & 0x3;
  312.     NXRect from;
  313.     NXPoint pos;
  314.     
  315.     [super renderAt:posx :posy move:moveOk];
  316.  
  317.     // decide to draw invisible or eye ghosts if necessary
  318.     if (eatable && ((powerState != DOT_FADING) || (img & 0x4)))
  319.         col = INVISIBLE_GHOST;
  320.     else if (state == HOME) col = GHOST_EYES;
  321.     NXSetRect(&from,
  322.         ((++img >> 1) & 0x3) * GHOST_SIZE,    col * GHOST_SIZE,
  323.         GHOST_SIZE, GHOST_SIZE);
  324.     pos.x = myX + posx; pos.y = myY + posy;
  325.     [ghosts composite:NX_SOVER fromRect:&from toPoint:&pos];
  326.     if (![[NXApp delegate] paused]) {
  327.         if (((++powerTime) >> 4) > flash_ticks[[[NXApp delegate] level]])
  328.             powerState = DOT_FADING;
  329.         if ((powerTime >> 4) > off_ticks[[[NXApp delegate] level]])
  330.             [self powerDot:NO];
  331.     }
  332.     return self;
  333. }
  334.  
  335.  
  336. @end
  337.